package info.niwota.vi;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;

/**
 * @author qiangli
 * 
 */
public class VoiceInputActivity extends Activity implements TextWatcher,
		OnClickListener {

	private static final CharSequence KB_COMMA = ",";

	private static final CharSequence KB_LF = "\n";

	private static final CharSequence KB_PERIOD = ".";

	private static final CharSequence KB_SPACE = " ";

	private static final int RC_INPUT = 1;

	private static final String LOG_TAG = "VoiceInputActivity";

	private static final String VI_HELP_URI = "file:///android_asset/vi.html";

	private static final String SAVED_TEXT = "save.text";

	private static final int DIALOG_WEB = 10;

	private static final String SEARCH_DEFAULT = "http://www.google.com/search?q=";

	private boolean autoediting;

	private CharSequence input;

	private CharSequence oldText;

	private ImageButton redoButton;

	private EditText text;

	private ImageButton undoButton;

	private String newText;

	private int oldStart;

	private int oldEnd;

	private int newStart;

	private int newEnd;

	private TextView title;

	private Button outButton;

	private boolean done;

	private View actionBar;

	private Button clearButton;

	private String lastUri = null;

	private MenuItem miPaste;

	private MenuItem miCopy;

	private MenuItem miClear;

	public void afterTextChanged(Editable arg) {
		toggleButton();
		//
		if (autoediting) {
			return;
		}
		input = null;
		disableRedoActions();
	}

	private void toggleButton() {
		int c = text.getText().length();
		boolean b = (c > 0);
		outButton.setEnabled(b);
		clearButton.setEnabled(b);
		actionBar.setVisibility(b ? View.VISIBLE : View.GONE);
	}

	private void autoAppendInput() {
		if (TextUtils.isEmpty(input)) {
			return;
		}
		autoediting = true;
		int st = text.getSelectionStart();
		int en = text.getSelectionEnd();
		if (st == -1 || en == -1) {
			text.append(input);
		} else {
			if (st > en) {
				// swap
				int x = st;
				st = en;
				en = x;
			}
			text.getEditableText().replace(st, en, input);
		}
		newText = text.getEditableText().toString();
		newStart = text.getSelectionStart();
		newEnd = text.getSelectionEnd();
		autoediting = false;
		//
		undoButton.setVisibility(View.VISIBLE);
		undoButton.setEnabled(true);
		redoButton.setVisibility(View.GONE);
		redoButton.setEnabled(false);
	}

	private void autoEdit(CharSequence s, int st, int en) {
		autoediting = true;
		//
		text.setText(s);
		if (st < 0 || en < 0 || st > en) {
			st = s.length();
			en = st;
		}
		//
		text.setSelection(st, en);
		//
		autoediting = false;
	}

	public void beforeTextChanged(CharSequence s, int start, int count,
			int after) {
	}

	private void disableRedoActions() {
		redoButton.setVisibility(View.GONE);
		undoButton.setVisibility(View.GONE);
		redoButton.setEnabled(false);
		undoButton.setEnabled(false);
	}

	private void doBS() {
		int st = text.getSelectionStart();
		int en = text.getSelectionEnd();
		if (st < 0 || en <= 0) {
			// en == 0, legitimate but can bs no more
			return;
		}
		st = (st == en ? st - 1 : st);
		//
		if (st > en) {
			// swap
			int x = st;
			st = en;
			en = x;
		}
		text.getEditableText().delete(st, en);
	}

	private void doKB(CharSequence key) {
		int st = text.getSelectionStart();
		int en = text.getSelectionEnd();
		if (st == -1 || en == -1) {
			text.append(key);
		} else {
			if (st > en) {
				// swap
				int x = st;
				st = en;
				en = x;
			}
			text.getEditableText().replace(st, en, key);
		}
	}

	private void doRedo() {
		autoEdit(newText, newStart, newEnd);
		//
		undoButton.setVisibility(View.VISIBLE);
		undoButton.setEnabled(true);
		redoButton.setVisibility(View.GONE);
		redoButton.setEnabled(false);
	}

	private void doSpeak() {
		startVoiceRecognitionActivity();
	}

	private void doUndo() {
		autoEdit(oldText, oldStart, oldEnd);
		//
		undoButton.setVisibility(View.GONE);
		undoButton.setEnabled(false);
		redoButton.setVisibility(View.VISIBLE);
		redoButton.setEnabled(true);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == RC_INPUT && resultCode == RESULT_OK) {
			ArrayList<String> words = data
					.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
			if (words == null) {
				return;
			}
			int len = words.size();
			if (len == 0) {
				return;
			}
			if (Debug.DEBUG) {
				Log.d(LOG_TAG, "words: " + words);
			}
			StringBuilder sb = new StringBuilder();
			for (String w : words) {
				sb.append(w);
				sb.append(KB_SPACE);
			}
			//
			CharSequence cs = normalize(sb);
			handleInput(cs);
		}
	}

	private void handleInput(CharSequence in) {
		input = in;
		oldText = text.getText().toString();
		oldStart = text.getSelectionStart();
		oldEnd = text.getSelectionEnd();
		//
		autoAppendInput();
	}

	private CharSequence normalize(StringBuilder sb) {
		try {
			String[] sa = sb.toString().split("\\s");
			if (sa.length > 0) {
				String p = sa[0];
				int pl = p.length();
				int len = sa.length;
				sb.setLength(0); // reuse sb
				sb.append(p);
				// remove space between single letter words
				for (int i = 1; i < len; i++) {
					String w = sa[i];
					int wl = w.length();
					if (wl > 1 || pl > 1) {
						sb.append(KB_SPACE);
					}
					pl = wl;
					sb.append(w);
				}
				return sb.toString();
			}
		} catch (Exception e) {
			// discard
		}
		return null;
	}

	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.voiceinput_title:
			help();
			return;
		case R.id.voiceinput_speak:
			doSpeak();
			return;
		case R.id.voiceinput_bs:
			doBS();
			return;
		case R.id.voiceinput_comma:
			doKB(KB_COMMA);
			return;
		case R.id.voiceinput_sp:
			doKB(KB_SPACE);
			return;
		case R.id.voiceinput_period:
			doKB(KB_PERIOD);
			return;
		case R.id.voiceinput_lf:
			doKB(KB_LF);
			return;
		case R.id.voiceinput_undo:
			doUndo();
			return;
		case R.id.voiceinput_redo:
			doRedo();
			return;
		case R.id.voiceinput_button_clear:
			doClear();
			return;
		case R.id.voiceinput_action_mail: {
			String s = text.getText().toString();
			AppUtils.startSendActivity(this, s);
			return;
		}
		case R.id.voiceinput_action_search: {
			onSearchRequested();
			return;
		}
		case R.id.voiceinput_action_text: {
			String s = text.getText().toString();
			AppUtils.startMmsActivity(this, s);
			return;
		}
		case R.id.voiceinput_action_web_redo: {
			if (TextUtils.isEmpty(lastUri)) {
				doGoto();
			} else {
				doWeb(lastUri );
			}
			return;
		}
		case R.id.voiceinput_action_web: {
			showDialog(DIALOG_WEB);
			return;
		}
		case R.id.voiceinput_button_in: {
			doPaste();
			return;
		}
		case R.id.voiceinput_button_out: {
			doCopy();
			return;
		}
		case R.id.voiceinput_button_done: {
			doQuit();
			return;
		}
		case R.id.voiceinput_use: {
			doAccept();
			break;
		}
		case R.id.voiceinput_discard: {
			doDiscard();
			break;
		}
		default:
		}
	}

	private void doDiscard() {
		setResult(RESULT_CANCELED);
		finish();
	}

	private void doAccept() {
		Intent data = new Intent();
		data.putExtra(Intents.EXTRA_INPUT, text.getText().toString());
		setResult(RESULT_OK, data);
		finish();
	}

	private void doQuit() {
		done = true;
		Utils.removeNotification(this);
		finish();
	}

	private void doCopy() {
		// TODO get selected text
		String s = text.getText().toString();
		if (!TextUtils.isEmpty(s)) {
			Utils.copyToClipboard(this, s);
		}
	}

	private void doPaste() {
		String s = Utils.getFromClipboard(this);
		if (s != null) {
			handleInput(s);
		}
	}

	private void doGoto() {
		// show web page if the input is a valid url,
		// otherwise do a default search
		String s = AppUtils.normalize(text.getText().toString());
		URL url = null;
		try {
			final String http = "http://";
			final String https = "https://";
			if (s.startsWith(http) || s.startsWith(https)) {
				url = new URL(s);
			} else {
				url = new URL(http + s);
			}
		} catch (MalformedURLException e) {
			//
		}
		if (url != null) {
			AppUtils.startBrowseActivity(VoiceInputActivity.this, url
					.toString());
		} else {
			String uri = SEARCH_DEFAULT + URLEncoder.encode(s);
			AppUtils.startBrowseActivity(this, uri);
		}
	}

	private void doWeb(String u) {
		lastUri = u;
		//
		String s = AppUtils.normalize(text.getText().toString());
		String uri = u + URLEncoder.encode(s);
		if (Debug.DEBUG) {
			Log.d(LOG_TAG, "doWeb uri: " + uri);
		}
		AppUtils.startBrowseActivity(this, uri);
	}

	private void doClear() {
		text.setText("");
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_WEB:
			return new AlertDialog.Builder(this).setTitle("").setItems(
					R.array.dialog_website_titles,
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int which) {
							String[] items = getResources().getStringArray(
									R.array.dialog_website_urls);
							if (which == 0) {
								doGoto();
								return;
							}
							//
							if (which > 0) {
								String u = items[which];
								doWeb(u);
							}
						}
					}).create();
		default:
		}
		return null;
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//
		setTheme(android.R.style.Theme_Translucent_NoTitleBar);
		requestWindowFeature(Window.FEATURE_NO_TITLE);

		setContentView(R.layout.voiceinput);
		//
		initRes();
		//
		if (Debug.DEBUG) {
			Log.d(LOG_TAG, "onCreate " + savedInstanceState);
		}
		if (savedInstanceState == null) {
			Intent i = getIntent();
			init(i);
		} else {
			restore(savedInstanceState);
		}
		//
		Utils.addNotification(this);
		toggleButton();
	}

	private void initRes() {
		title = (TextView) findViewById(R.id.voiceinput_title);
		text = (EditText) findViewById(R.id.voiceinput_text);
		redoButton = (ImageButton) findViewById(R.id.voiceinput_redo);
		undoButton = (ImageButton) findViewById(R.id.voiceinput_undo);
		//
		Button inButton = (Button) findViewById(R.id.voiceinput_button_in);
		outButton = (Button) findViewById(R.id.voiceinput_button_out);
		clearButton = (Button) findViewById(R.id.voiceinput_button_clear);
		Button doneButton = (Button) findViewById(R.id.voiceinput_button_done);

		inButton.setEnabled(true);
		outButton.setEnabled(true);
		clearButton.setEnabled(true);
		doneButton.setEnabled(true);
		//
		Button okButton = (Button) findViewById(R.id.voiceinput_use);
		Button discardButton = (Button) findViewById(R.id.voiceinput_discard);
		//
		View kb = findViewById(R.id.voiceinput_keyboard);
		Button speakButton = (Button) findViewById(R.id.voiceinput_speak);
		Button bsButton = (Button) findViewById(R.id.voiceinput_bs);
		Button commaButton = (Button) findViewById(R.id.voiceinput_comma);
		Button spButton = (Button) findViewById(R.id.voiceinput_sp);
		Button periodButton = (Button) findViewById(R.id.voiceinput_period);
		Button lfButton = (Button) findViewById(R.id.voiceinput_lf);
		//
		okButton.setEnabled(true);
		discardButton.setEnabled(true);
		//
		disableRedoActions();
		//
		boolean canSpeak = Utils.canSpeak(this);
		kb.setVisibility(canSpeak ? View.VISIBLE : View.GONE);
		if (canSpeak) {
			speakButton.setOnClickListener(this);
			bsButton.setOnClickListener(this);
			commaButton.setOnClickListener(this);
			spButton.setOnClickListener(this);
			periodButton.setOnClickListener(this);
			lfButton.setOnClickListener(this);

			undoButton.setOnClickListener(this);
			redoButton.setOnClickListener(this);
		} else {
			speakButton.setEnabled(false);
			bsButton.setEnabled(false);
			commaButton.setEnabled(false);
			spButton.setEnabled(false);
			periodButton.setEnabled(false);
			lfButton.setEnabled(false);

			redoButton.setEnabled(false);
			undoButton.setEnabled(false);
		}
		//
		title.setOnClickListener(this); // help

		//
		inButton.setOnClickListener(this);
		outButton.setOnClickListener(this);
		clearButton.setOnClickListener(this);
		doneButton.setOnClickListener(this);
		//
		okButton.setOnClickListener(this);
		discardButton.setOnClickListener(this);
		//
		text.addTextChangedListener(this);
		//
		// init actions
		final int[] actions = { R.id.voiceinput_action_web_redo,
				R.id.voiceinput_action_web, R.id.voiceinput_action_mail,
				R.id.voiceinput_action_text, R.id.voiceinput_action_search };
		for (int i = 0; i < actions.length; i++) {
			ImageButton btn = (ImageButton) findViewById(actions[i]);
			btn.setOnClickListener(this);
		}
		actionBar = findViewById(R.id.voiceinput_actions);
	}

	private void restore(Bundle i) {
		String s = i.getString(SAVED_TEXT);
		if (!TextUtils.isEmpty(s)) {
			autoEdit(s, -1, -1);
		}
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			done = false;
			finish();
			return true;
		} else {
			return super.onKeyDown(keyCode, event);
		}
	}

	private void init(Intent i) {
		if (i == null) {
			return;
		}
		String s = i.getStringExtra(Intents.EXTRA_INPUT);
		if (TextUtils.isEmpty(s)) {
			// try last saved
			s = Utils.read(this);
		}
		//
		if (!TextUtils.isEmpty(s)) {
			autoEdit(s, -1, -1); // s.length
		}

		String t = i.getStringExtra(Intents.EXTRA_TITLE);
		if (TextUtils.isEmpty(t)) {
			title.setText(R.string.text_voiceinput);
		} else {
			title.setText(t);
		}
		//
		String h = i.getStringExtra(Intents.EXTRA_HINT);
		if (!TextUtils.isEmpty(h)) {
			text.setHint(h);
		}
	}

	public void onTextChanged(CharSequence s, int start, int before, int count) {
	}

	private void startVoiceRecognitionActivity() {
		String ps = getString(R.string.text_voiceinput_prompt_speak);
		Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
		intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
				RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
		intent.putExtra(RecognizerIntent.EXTRA_PROMPT, ps);
		startActivityForResult(intent, RC_INPUT);
	}

	@Override
	protected void onPause() {
		super.onPause();
		//
		if (!done) {
			Utils.addNotification(this);
			String data = text.getText().toString();
			Utils.write(this, data);
		} else {
			// clear
			Utils.write(this, "");
		}
		Preferences.putLastUri(this, lastUri);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		if (Debug.DEBUG) {
			Log.d(LOG_TAG, "onSaveInstanceState " + outState);
		}
		String s = text.getText().toString();
		outState.putString(SAVED_TEXT, s);
		//
		super.onSaveInstanceState(outState);
	}

	@Override
	protected void onResume() {
		super.onResume();
		done = false;
		//
		lastUri = Preferences.getLastUri(this);
		if (lastUri == null) {
			lastUri = SEARCH_DEFAULT;
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	public boolean onSearchRequested() {
		// startSearch(null, false, null, false);
		String q = text.getText().toString();
		startSearch(q, false, null, true);
		return true;
	}

	@Override
	public void startSearch(String initialQuery, boolean selectInitialQuery,
			Bundle appSearchData, boolean globalSearch) {
		super.startSearch(initialQuery, selectInitialQuery, appSearchData,
				globalSearch);
	}

	private void help() {
		try {
			Intent i = new Intent(this, WebViewActivity.class);
			i.setData(Uri.parse(VI_HELP_URI));
			startActivity(i);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater mif = getMenuInflater();
		mif.inflate(R.menu.vi_options, menu);
		//
		miPaste = menu.findItem(R.id.vi_paste_opt_menu_id);
		miCopy = menu.findItem(R.id.vi_copy_opt_menu_id);
		miClear = menu.findItem(R.id.vi_clear_opt_menu_id);
		//
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		switch (id) {
		case R.id.vi_paste_opt_menu_id:
			doPaste();
			break;
		case R.id.vi_copy_opt_menu_id:
			doCopy();
			break;
		case R.id.vi_clear_opt_menu_id:
			doClear();
			break;
		case R.id.vi_help_opt_menu_id:
			help();
			break;
		case R.id.vi_quit_opt_menu_id:
			doQuit();
			break;
		default:
			return false;
		}
		return true;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		boolean cb = Utils.hasClipboardText(this);
		miPaste.setEnabled(cb);
		//
		String s = text.getText().toString();
		boolean ct = (!TextUtils.isEmpty(s));
		miCopy.setEnabled(ct);
		miClear.setEnabled(ct);
		//
		return true;
	}
	
	
}
